#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/types.h>

#define SIZE 512
#define NUM 32
#define ZERO '\0'
#define SEP " "
#define SkipPath(p) do {p+=strlen(p)-1; while(*p!='/') p--; }while(0)

#define No_Redir 0
#define In_Redir 1
#define Out_Redir 2
#define App_Redir 3
#define Skip_Space(cmd,pos) do{ while(cmd[pos] == ' ') pos++; }while(0)
int redir_type = No_Redir;
char* filename = NULL;

char* Argv[NUM];
char cwd[SIZE*2];
int lastcode=0;
const char* Getcwd()
{
    char* cwd = getenv("PWD");
    if(cwd == NULL) return "None";
    else return cwd;
}
const char* GetUserName()
{
    char* user = getenv("USER");
    if(user == NULL) return "None";
    else return user;
}
const char* GetHostName()
{
    char* hostname = getenv("HOSTNAME");
    if(hostname == NULL) return "None";
    else return hostname;
}
const char* GetHome()
{
    char* home = getenv("HOME");
    if(home == NULL) return "/";
    else return home;
}

void MakeCommand_andPrint()
{
    const char* UserName = GetUserName();
    const char* HostName = GetHostName();
    const char* cwd = Getcwd();

    char line[SIZE];
    SkipPath(cwd);
    snprintf(line,sizeof(line),"[%s@%s %s]> ",UserName,HostName,strlen(cwd) == 1? "/":cwd+1);
    printf("%s",line);
    fflush(stdout);
}

int GetUserCommand(char command[],size_t n)
{
    char* s = fgets(command,n,stdin);
    if(s == NULL) return -1;
    command[strlen(command)-1] = ZERO;
    return strlen(command);
}

void SplitCommand(char command[],size_t n)
{
    (void)n;
    Argv[0] = strtok(command,SEP);
    int index = 1;
    while((Argv[index++] = strtok(NULL,SEP)));

}

void ExcuteCommand()
{
    pid_t id = fork();
    if(id < 0) exit(-1);
    if(id == 0)
    {
        int fd = 0;
        mode_t mode = 0666;
        if(filename)
        {
             if(redir_type == In_Redir)
             {
                 fd = open(filename,O_RDONLY,mode);
                 dup2(fd,0);
             }
             else if(redir_type == Out_Redir)
             {
                 fd = open(filename,O_WRONLY | O_CREAT | O_TRUNC,mode);
                 dup2(fd,1);
             }
             else if(redir_type == App_Redir)
             {
                 fd = open(filename,O_WRONLY | O_CREAT | O_APPEND,mode);
                 dup2(fd,1);
             }
             else{
             }
        }
        execvp(Argv[0],Argv);
        exit(errno);
    }
    else
    {
        //等待,防止内存泄露
        int status = 0;
        pid_t rid = waitpid(id,&status,0);
        if(rid > 0)
        {
            lastcode = WEXITSTATUS(status); //退出码
            if(lastcode != 0) printf("%s:%s:%d\n",Argv[0],strerror(lastcode),lastcode);
        }
    }
}

void CD()
{
    const char* path = Argv[1];
    if(path == NULL) path = GetHome();
    chdir(path);

    //修改PWD
    char temp[SIZE*2];
    getcwd(temp,sizeof(temp));
    snprintf(cwd,sizeof(cwd),"PWD=%s",temp);

    putenv(cwd);
}
int CheckBuildin()
{
    int yes = 0;
    if(strcmp(Argv[0],"cd") == 0)
    {
        yes = 1;
        CD();
    }
    else if(strcmp(Argv[0],"echo") == 0 && strcmp(Argv[1],"$?") == 0)
    {
        yes = 1;
        printf("%d\n",lastcode);
        lastcode = 0;
    }
    return yes;
}
void CheckRedir(char cmd[])
{
    //"ls -l -a > log.txt"
    int pos = 0;
    int end = strlen(cmd);
    while(pos < end)
    {
        if(cmd[pos] == '>')
        {
            if(cmd[pos+1] == '>')
            {
                cmd[pos++] = 0;
                pos++;
                redir_type = App_Redir;
                Skip_Space(cmd,pos);
                filename = cmd+pos;
            }
            else{
                cmd[pos++] = 0;
                redir_type = Out_Redir;
                Skip_Space(cmd,pos);
                filename = cmd+pos;
            }
        }
        else if(cmd[pos] == '<')
        {
            cmd[pos++] = 0;
            redir_type = In_Redir;
            Skip_Space(cmd,pos);
            filename = cmd+pos;
        }
        else{
            pos++;
        }
    }
}
int main()
{
    while(1)
    {

        redir_type = No_Redir;
        filename = NULL;
        //1.打印自己的命令行
        MakeCommand_andPrint();

        //2.获取用户的命令行
        char command[SIZE];
        int n = GetUserCommand(command,sizeof(command));
        if(n <= 0) return -1;

        //2.1 checkRedir
        CheckRedir(command);

        //2.2.dubug
       // printf("filename:%s\n",filename);
       // printf("cmd:%s\n",command);
        //3.分割命令行
        SplitCommand(command,sizeof(command));

        //4.判断是否是内建命令
        n = CheckBuildin();
        if(n) continue;
        //5.执行命令
        ExcuteCommand();
    }
    return 0;
}
